package com.uduemc.biso.node.web.api.controller;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.uduemc.biso.core.extities.center.Host;
import com.uduemc.biso.core.utils.DomainUtil;
import com.uduemc.biso.core.utils.JsonResult;
import com.uduemc.biso.core.utils.RedisUtil;
import com.uduemc.biso.node.core.common.entities.SSL;
import com.uduemc.biso.node.core.entities.HDomain;
import com.uduemc.biso.node.core.node.extities.DomainRedirectList;
import com.uduemc.biso.node.web.api.component.RequestHolder;
import com.uduemc.biso.node.web.api.service.DomainService;
import com.uduemc.biso.node.web.api.service.HostSetupService;
import com.uduemc.biso.node.web.component.CenterFunction;
import com.uduemc.biso.node.web.utils.ResubmitUtil;
import io.swagger.annotations.*;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;

@RestController
@RequestMapping("/api/domain")
@Api(tags = "域名")
public class DomainController {

    @Resource
    private DomainService domainServiceImpl;

    @Resource
    private RequestHolder requestHolder;

    @Resource
    private HostSetupService hostSetupServiceImpl;

    @Resource
    private CenterFunction centerFunction;

    @Resource
    private RedisUtil redisUtil;

    /**
     * 域名列表数据
     *
     * @return
     * @throws JsonParseException
     * @throws JsonMappingException
     * @throws JsonProcessingException
     * @throws IOException
     */
    @PostMapping("/infos-list")
    @ApiOperation(value = "所有域名列表", notes = "获取所有域名列表数据，包含系统默认域名")
    @ApiResponses({@ApiResponse(code = 200, message = "data 中数据同 policeRecord 为公安备案号信息")})
    public JsonResult infosList() throws IOException {
        List<HDomain> data = domainServiceImpl.allDomain();
        return JsonResult.ok(data);
    }

    /**
     * 域名列表数据
     *
     * @return
     * @throws JsonParseException
     * @throws JsonMappingException
     * @throws JsonProcessingException
     * @throws IOException
     */
    @PostMapping("/bind-infos-list")
    @ApiOperation(value = "绑定域名列表", notes = "获取所有绑定的域名列表数据，不包含系统默认域名")
    public JsonResult bindInfosList() throws IOException {
        List<HDomain> data = domainServiceImpl.bindIndos();
        return JsonResult.ok(data);
    }

    /**
     * 绑定域名数据
     *
     * @param domain
     * @return
     * @throws JsonParseException
     * @throws JsonMappingException
     * @throws JsonProcessingException
     * @throws IOException
     */
    @PostMapping("/bind")
    @ApiOperation(value = "绑定域名", notes = "通过传入域名参数绑定域名")
    @ApiImplicitParams({@ApiImplicitParam(name = "domain", value = "传递要绑定域名的参数", required = true)})
    synchronized public JsonResult bind(@RequestParam("domain") String domain, HttpServletRequest request) throws IOException {
        domain = StrUtil.trim(domain);
        if (StrUtil.isBlank(domain)) {
            return JsonResult.illegal();
        }
        Short trial = requestHolder.getHost().getTrial();
        if (trial == null || trial == (short) 0) {
            return JsonResult.messageError("试用站点无法绑定域名！");
        }
        boolean existDomainName = domainServiceImpl.isExistDomainName(domain);
        if (existDomainName) {
            return JsonResult.messageError("该域名已绑定，无法再次绑定！");
        }

        int dnum = hostSetupServiceImpl.getDnum();
        int usedDnum = hostSetupServiceImpl.getUsedDnum();
        if ((usedDnum + 1) > dnum) {
            return JsonResult.messageError("绑定域名数量上限， 最多只能绑定" + dnum + "个域名！");
        }

        String asciiDomain = DomainUtil.toASCII(domain);
        if (StrUtil.isBlank(asciiDomain)) {
            return JsonResult.messageError(domain + " 非有效域名。");
        }

        // 请求主控端，获取是否拥有绑定权限
        String errorMsg = centerFunction.bindDomain(requestHolder.getHost().getId(), domain);
        if (StrUtil.isNotBlank(errorMsg)) {
            return JsonResult.messageError(errorMsg);
        }

        boolean bindCurrentDomain = domainServiceImpl.bindCurrentDomain(domain);
        if (!bindCurrentDomain) {
            return JsonResult.assistance();
        }
        List<HDomain> data = domainServiceImpl.allDomain();
        AtomicReference<String> atomicDomain = new AtomicReference<>(domain);
        long count = data.stream().filter(e -> e.getDomainName().equals(atomicDomain.get())).count();
        if (count > 0L) {
            JsonResult result = JsonResult.messageSuccess("绑定域名成功！", data);
            redisUtil.set(ResubmitUtil.redisKey(request), result, 10L);
            return result;
        }
        return JsonResult.messageError("绑定域名失败！", data);
    }

    /**
     * SSL证书绑定
     *
     * @param domainId
     * @param resourcePrivatekeyId
     * @param resourceCertificateId
     * @return
     * @throws JsonParseException
     * @throws JsonMappingException
     * @throws JsonProcessingException
     * @throws IOException
     */
    @PostMapping("/bind-ssl")
    @ApiOperation(value = "SSL证书绑定", notes = "对应的域名绑定证书")
    @ApiImplicitParams({@ApiImplicitParam(name = "domainId", value = "域名数据ID", required = true),
            @ApiImplicitParam(name = "resourcePrivatekeyId", value = "私钥文件 资源ID", required = true),
            @ApiImplicitParam(name = "resourceCertificateId", value = "证书文件 资源ID", required = true),
            @ApiImplicitParam(name = "httpsOnly", value = "是否通过301的方式只保留 https 的访问， 0-否 1-是，默认1", required = false)})
    @ApiResponses({@ApiResponse(code = 200, message = "data.status 表示当前状态，默认 -1,(-1)-初始数据常状态 1-正常 2-私钥与证书不匹配 3-Web服务异常，需要联系管理员\n"
            + "data.hssl 域名绑定证书的数据，其中 data.hssl.id 用于删除\n" + "data.hssl.httpsOnly 是否通过301的方式只保留 https 的访问， 0-否 1-是\n" + "data.domain 域名数据信息，其中domainName表示域名\n"
            + "data.privatekey 绑定证书私钥的资源数据，其中originalFilename表示证书私钥文件名\n" + "data.certificate 绑定证书的资源数据，其中originalFilename表示证书文件名\n")})
    public JsonResult bindSSL(@RequestParam("domainId") long domainId, @RequestParam("resourcePrivatekeyId") long resourcePrivatekeyId,
                              @RequestParam("resourceCertificateId") long resourceCertificateId,
                              @RequestParam(value = "httpsOnly", required = false, defaultValue = "1") short httpsOnly)
            throws IOException {
        return domainServiceImpl.bindSSL(domainId, resourcePrivatekeyId, resourceCertificateId, httpsOnly);
    }

    /**
     * 通过 domainId 获取数据，修改 h_ssl.https_only 字段
     *
     * @param domainId
     * @param httpsOnly
     * @return
     * @throws JsonParseException
     * @throws JsonMappingException
     * @throws JsonProcessingException
     * @throws IOException
     */
    @PostMapping("/update-https-only")
    @ApiOperation(value = "修改强制HTTPS访问", notes = "通过 domainId 获取绑定证书的域名数据，并进行校验。通过后修改是否强制https访问")
    @ApiImplicitParams({@ApiImplicitParam(name = "domainId", value = "域名数据ID", required = true),
            @ApiImplicitParam(name = "httpsOnly", value = "是否通过301的方式只保留 https 的访问， 0-否 1-是", required = true)})
    @ApiResponses({@ApiResponse(code = 200, message = "返回数据格式同 SSL证书绑定（/bind-ssl）接口")})
    public JsonResult updateHttpsOnly(@RequestParam("domainId") long domainId, @RequestParam("httpsOnly") short httpsOnly)
            throws IOException {
        return domainServiceImpl.updateHttpsOnly(domainId, httpsOnly);
    }

    /**
     * 获取单个域名绑定ssl证书的数据信息
     *
     * @param domainId
     * @return
     * @throws JsonParseException
     * @throws JsonMappingException
     * @throws JsonProcessingException
     * @throws IOException
     */
    @PostMapping("/info-ssl")
    @ApiOperation(value = "单个域名绑定SSL证书信息", notes = "获取单个域名绑定ssl证书的数据信息")
    @ApiImplicitParams({@ApiImplicitParam(name = "domainId", value = "域名数据ID", required = true)})
    @ApiResponses({@ApiResponse(code = 200, message = "data.status 表示当前状态，默认 -1,(-1)-初始数据常状态 1-正常 2-私钥与证书不匹配 3-Web服务异常，需要联系管理员\n"
            + "data.hssl 域名绑定证书的数据，其中 data.hssl.id 用于删除\n" + "data.domain 域名数据信息，其中domainName表示域名\n"
            + "data.privatekey 绑定证书私钥的资源数据，其中originalFilename表示证书私钥文件名\n" + "data.certificate 绑定证书的资源数据，其中originalFilename表示证书文件名\n")})
    public JsonResult infoSSL(@RequestParam("domainId") long domainId) throws IOException {
        SSL sslInfo = domainServiceImpl.infoSSL(domainId);
        return JsonResult.ok(sslInfo);
    }

    /**
     * 获取站点下所有绑定SSL证书数据列表
     *
     * @return
     * @throws JsonParseException
     * @throws JsonMappingException
     * @throws JsonProcessingException
     * @throws IOException
     */
    @PostMapping("/infos-ssl")
    @ApiOperation(value = "绑定SSL证书的数据列表", notes = "获取站点下所有绑定SSL证书数据列表")
    @ApiResponses({@ApiResponse(code = 200, message = "data.status 表示当前状态，默认 -1,(-1)-初始数据常状态 1-正常 2-私钥与证书不匹配 3-Web服务异常，需要联系管理员\n"
            + "data.hssl 域名绑定证书的数据，其中 data.hssl.id 用于删除\n" + "data.domain 域名数据信息，其中domainName表示域名\n"
            + "data.privatekey 绑定证书私钥的资源数据，其中originalFilename表示证书私钥文件名\n" + "data.certificate 绑定证书的资源数据，其中originalFilename表示证书文件名\n")})
    public JsonResult infosSSL() throws IOException {
        return JsonResult.ok(domainServiceImpl.infosSSL());
    }

    /**
     * 通过传入参数 sslId ，对应 /domain/info-ssl 返回SSL数据中的 hssl.id，删除后返回被删除的 SSL 数据
     *
     * @param sslId
     * @return
     * @throws JsonParseException
     * @throws JsonMappingException
     * @throws JsonProcessingException
     * @throws IOException
     */
    @PostMapping("/delete-ssl")
    @ApiOperation(value = "删除绑定SSL证书数据", notes = "通过传入参数 sslId ，对应 /domain/info-ssl 返回SSL数据中的 hssl.id，删除后返回被删除的 SSL 数据")
    @ApiResponses({@ApiResponse(code = 200, message = "data.status 表示当前状态，默认 -1,(-1)-初始数据常状态 1-正常 2-私钥与证书不匹配 3-Web服务异常，需要联系管理员\n"
            + "data.hssl 域名绑定证书的数据，其中 data.hssl.id 用于删除\n" + "data.domain 域名数据信息，其中domainName表示域名\n"
            + "data.privatekey 绑定证书私钥的资源数据，其中originalFilename表示证书私钥文件名\n" + "data.certificate 绑定证书的资源数据，其中originalFilename表示证书文件名\n")})
    public JsonResult deleteSSL(@RequestParam("sslId") long sslId, HttpServletRequest request) throws IOException {
        JsonResult result = JsonResult.ok(domainServiceImpl.deleteSSL(sslId));
        redisUtil.set(ResubmitUtil.redisKey(request), result, 3L);
        return result;
    }

    /**
     * 删除域名数据
     *
     * @param id
     * @return
     * @throws JsonParseException
     * @throws JsonMappingException
     * @throws JsonProcessingException
     * @throws IOException
     */
    @PostMapping("/delete-item")
    @ApiOperation(value = "删除域名", notes = "传入域名的数据ID，删除域名")
    @ApiImplicitParams({@ApiImplicitParam(name = "id", value = "域名数据ID", required = true)})
    public JsonResult deleteItem(@RequestParam("id") long id) throws IOException {
        if (id < 1) {
            return JsonResult.illegal();
        }
        HDomain hDomain = domainServiceImpl.getCurrentInfoById(id);
        if (hDomain == null) {
            return JsonResult.illegal();
        }
        if (hDomain.getDomainType() != null && hDomain.getDomainType().shortValue() == 0) {
            return JsonResult.messageError("系统域名不能删除！");
        }
        boolean deleteItem = domainServiceImpl.deleteItem(hDomain);
        if (!deleteItem) {
            return JsonResult.assistance();
        }

        // 请求主控端，解绑域名
        centerFunction.unbindDomain(requestHolder.getHost().getId(), hDomain.getDomainName());

        return JsonResult.ok(hDomain);
    }

    /**
     * 重定向数据列表
     *
     * @return
     * @throws JsonParseException
     * @throws JsonMappingException
     * @throws JsonProcessingException
     * @throws IOException
     */
    @PostMapping("/redirect-list")
    @ApiOperation(value = "重定向域名列表", notes = "获取重定向域名的数据列表")
    public JsonResult redirectList() throws IOException {
        DomainRedirectList data = domainServiceImpl.getCurrentRedirectLIst();
        return JsonResult.ok(data);
    }

    /**
     * 绑定重定向域名
     *
     * @param id
     * @param redirectId
     * @return
     * @throws JsonParseException
     * @throws JsonMappingException
     * @throws JsonProcessingException
     * @throws IOException
     */
    @PostMapping("/bind-redirect")
    @ApiOperation(value = "绑定重定向域名", notes = "通过传入两个域名的ID参数，绑定域名的重定向")
    @ApiImplicitParams({@ApiImplicitParam(name = "id", value = "要重定向的域名数据ID", required = true),
            @ApiImplicitParam(name = "redirectId", value = "重定向到域名数据ID", required = true)})
    public JsonResult bindRedirect(@RequestParam("id") long id, @RequestParam("redirectId") long redirectId)
            throws IOException {
        HDomain from = domainServiceImpl.getCurrentInfoById(id);
        if (from == null) {
            return JsonResult.illegal();
        }
        HDomain to = domainServiceImpl.getCurrentInfoById(redirectId);
        if (to == null) {
            return JsonResult.illegal();
        }
        if (to.getRedirect() != null && to.getRedirect().longValue() > 0) {
            return JsonResult.illegal();
        }

        from.setRedirect(redirectId);
        boolean updateItem = domainServiceImpl.updateItem(from);
        if (!updateItem) {
            return JsonResult.assistance();
        }

        return JsonResult.ok(id);
    }

    @PostMapping("/delete-redirect")
    @ApiOperation(value = "解除域名重定向", notes = "通过域名数据ID参数，解除域名重定向")
    @ApiImplicitParams({@ApiImplicitParam(name = "id", value = "解除重定向的域名数据ID", required = true)})
    public JsonResult deleteRedirect(@RequestParam("id") long id) throws IOException {
        HDomain from = domainServiceImpl.getCurrentInfoById(id);
        if (from == null || from.getRedirect() == null || from.getRedirect().longValue() == 0) {
            return JsonResult.illegal();
        }
        from.setRedirect(0L);
        boolean updateItem = domainServiceImpl.updateItem(from);
        if (!updateItem) {
            return JsonResult.assistance();
        }
        return JsonResult.ok(id);
    }

    @PostMapping("/redirect-alert")
    @ApiOperation(value = "是否弹窗系统域名重定向", notes = "绑定域名后请求次接口。逻辑：是否首次绑定域名，是否正式用户且审核通过的站点。")
    @ApiResponses({@ApiResponse(code = 200, message = "data 的结果说明：0-不弹窗 1-弹窗")})
    public JsonResult redirectAlert() throws IOException {
        List<HDomain> allDomain = domainServiceImpl.allDomain();
        if (CollUtil.size(allDomain) == 2) {
            Host host = requestHolder.getHost();
            Short trial = host.getTrial();
            Short review = host.getReview();
            if (trial != null && trial.shortValue() == (short) 1 && review != null && review.shortValue() == (short) 1) {
                return JsonResult.ok(1);
            }
        }
        return JsonResult.ok(0);
    }

    @PostMapping("/update-police-record")
    @ApiOperation(value = "修改域名公安备案号", notes = "通过参数，对域名的 公安备案号信息 进行修改")
    @ApiImplicitParams({@ApiImplicitParam(name = "id", value = "对应的域名数据ID", required = true),
            @ApiImplicitParam(name = "policeRecord", value = "域名公安备案号信息", required = true)})
    public JsonResult updatePoliceRecord(@RequestParam("id") long id, @RequestParam("policeRecord") String policeRecord)
            throws IOException {
        if (id < 1) {
            return JsonResult.illegal();
        }
        HDomain hDomain = domainServiceImpl.getCurrentInfoById(id);
        if (hDomain == null) {
            return JsonResult.illegal();
        }
        if (hDomain.getDomainType() != null && hDomain.getDomainType().shortValue() == 0) {
            return JsonResult.messageError("系统域名不能编辑公安备案信息！");
        }

        hDomain.setPoliceRecord(StrUtil.trim(policeRecord));
        boolean updateItem = domainServiceImpl.updateItem(hDomain);
        if (!updateItem) {
            return JsonResult.assistance();
        }
        return JsonResult.ok(hDomain);
    }
}
